using System.Security.Cryptography;
using System.Text;
using JetBrains.Annotations;

namespace System;
/// <summary>
/// 
/// </summary>
public static class StringExtensions
{
    /// <summary>
    /// Indicates whether this string is null or an System.String.Empty string.
    /// </summary>
    [ContractAnnotation("str:null => true")]
    public static bool IsNullOrEmpty(this string? str)
    {
        return string.IsNullOrEmpty(str);
    }

    /// <summary>
    /// indicates whether this string is null, empty, or consists only of white-space characters.
    /// </summary>
    [ContractAnnotation("str:null => true")]
    public static bool IsNullOrWhiteSpace(this string? str)
    {
        return string.IsNullOrWhiteSpace(str);
    }
    /// <summary>
    /// Gets a substring of a string from beginning of the string.
    /// </summary>
    /// <exception cref="ArgumentNullException">Thrown if <paramref name="str"/> is null</exception>
    /// <exception cref="ArgumentException">Thrown if <paramref name="len"/> is bigger that string's length</exception>
    public static string Left(this string str, int len)
    {
        Requires.NotNull(str, nameof(str));

        if (str.Length < len)
        {
            throw new ArgumentException("len argument can not be bigger than given string's length!");
        }

        return str.Substring(0, len);
    }
    /// <summary>
    /// Converts line endings in the string to <see cref="Environment.NewLine"/>.
    /// </summary>
    public static string NormalizeLineEndings(this string str)
    {
        return str.Replace("\r\n", "\n").Replace("\r", "\n").Replace("\n", Environment.NewLine);
    }

    /// <summary>
    /// Gets a substring of a string from end of the string.
    /// </summary>
    /// <exception cref="ArgumentNullException">Thrown if <paramref name="str"/> is null</exception>
    /// <exception cref="ArgumentException">Thrown if <paramref name="len"/> is bigger that string's length</exception>
    public static string Right(this string str, int len)
    {
        Requires.NotNull(str, nameof(str));

        if (str.Length < len)
        {
            throw new ArgumentException("len argument can not be bigger than given string's length!");
        }

        return str.Substring(str.Length - len, len);
    }

    /// <summary>
    /// Uses string.Split method to split given string by given separator.
    /// </summary>
    public static string[] Split(this string str, string separator)
    {
        return str.Split([separator], StringSplitOptions.None);
    }
    /// <summary>
    /// Uses string.Split method to split given string by given separator.
    /// </summary>
    public static string[] Split(this string str, string separator, StringSplitOptions options)
    {
        return str.Split([separator], options);
    }

    /// <summary>
    /// Uses string.Split method to split given string by <see cref="Environment.NewLine"/>.
    /// </summary>
    public static string[] SplitToLines(this string str)
    {
        return str.Split(Environment.NewLine);
    }
    /// <summary>
    /// Uses string.Split method to split given string by <see cref="Environment.NewLine"/>.
    /// </summary>
    public static string[] SplitToLines(this string str, StringSplitOptions options)
    {
        return str.Split(Environment.NewLine, options);
    }

    /// <summary>
    /// Converts string to enum value.
    /// </summary>
    /// <typeparam name="T">Type of enum</typeparam>
    /// <param name="value">String value to convert</param>
    /// <param name="result"></param>
    /// <returns>Returns enum object</returns>
    public static bool TryToEnum<T>(this string value, out T result)
        where T : struct
    {
        Requires.NotNull(value, nameof(value));

        return Enum.TryParse(value, out result);
    }
    /// <summary>
    /// Converts string to enum value.
    /// </summary>
    /// <typeparam name="T">Type of enum</typeparam>
    /// <param name="value">String value to convert</param>
    /// <param name="ignoreCase">Ignore case</param>
    /// <param name="result"></param>
    /// <returns>Returns enum object</returns>
    public static bool TryToEnum<T>(this string value, bool ignoreCase, out T result)
        where T : struct
    {
        Requires.NotNull(value, nameof(value));
        return Enum.TryParse(value, ignoreCase, out result);
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="str"></param>
    /// <returns></returns>
    public static string ToMd5(this string str)
    {
        using var md5 = MD5.Create();

        var inputBytes = Encoding.UTF8.GetBytes(str);
        var hashBytes = md5.ComputeHash(inputBytes);

        var sb = new StringBuilder();

        foreach (var hashByte in hashBytes)
        {
            sb.Append(hashByte.ToString("X2"));
        }

        return sb.ToString();
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="str"></param>
    /// <returns></returns>
    public static string ToSha1(this string str)
    {
        using var sha = SHA1.Create();

        var data = sha.ComputeHash(Encoding.UTF8.GetBytes(str));

        var sb = new StringBuilder();
        foreach (var d in data)
        {
            sb.Append(d.ToString("x2"));
        }

        return sb.ToString();
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="str"></param>
    /// <returns></returns>
    public static string ToSha256(this string str)
    {
        using var sha = SHA256.Create();

        var data = sha.ComputeHash(Encoding.UTF8.GetBytes(str));

        var sb = new StringBuilder();
        foreach (var d in data)
        {
            sb.Append(d.ToString("x2"));
        }

        return sb.ToString();
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="str"></param>
    /// <returns></returns>
    public static string ToSha384(this string str)
    {
        using var sha = SHA384.Create();

        var data = sha.ComputeHash(Encoding.UTF8.GetBytes(str));

        var sb = new StringBuilder();
        foreach (var d in data)
        {
            sb.Append(d.ToString("x2"));
        }
        return sb.ToString();
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="str"></param>
    /// <returns></returns>
    public static string ToSha512(this string str)
    {
        using var sha = SHA512.Create();

        var data = sha.ComputeHash(Encoding.UTF8.GetBytes(str));

        var sb = new StringBuilder();
        foreach (var d in data)
        {
            sb.Append(d.ToString("x2"));
        }
        return sb.ToString();
    }

    /// <summary>
    /// Converts given string to a byte array using <see cref="Encoding.UTF8"/> encoding.
    /// </summary>
    public static byte[] GetBytes(this string str)
    {
        return str.GetBytes(Encoding.UTF8);
    }
    /// <summary>
    /// Converts given string to a byte array using the given <paramref name="encoding"/>
    /// </summary>
    public static byte[] GetBytes([NotNull] this string str, [NotNull] Encoding encoding)
    {
        Requires.NotNull(str, nameof(str));
        Requires.NotNull(encoding, nameof(encoding));

        return encoding.GetBytes(str);
    }
}